package com.chen.function_demo.chapter2;

import java.util.*;
import java.util.function.*;

/**
 * 第二章第六节
 * @author 陈哲<p>
 * ================================<p>
 * Date: 2025/5/11<p>
 * Time: 23:04<p>
 * ================================
 */
public class Section6 {
    /*
        高阶函数：函数的参数或者返回值是函数，即其他函数对象的使用者
        作用：将通用的或复杂的逻辑隐含在高阶函数内，将易变的、未定的逻辑放在外部的函数对象中。
     */

    /**
     * 模仿stream，写一个简单的simpleStream，提供基本的高阶函数如map、filter等
     * SimpleStream.of(list)
     * .filter(x -> (x & 1) == 5)
     * .map(x -> x * x)
     * .forEach(System.out::println);
     */
    static class SimpleStream<T> {

        private final Collection<T> list;

        /**
         * 第一个实现of的方法（静态方法）
         * 注：类上定义的范型不能给静态方法用，静态方法需要自己再定义
         * @param list 数据
         * @param <T>  范型
         * @return simpleStream
         */
        public static <T> SimpleStream<T> of(Collection<T> list) {
            return new SimpleStream<T>(list);
        }

        //
        @SafeVarargs
        public static <T> SimpleStream<T> of(T... values) {
            return new SimpleStream<>(values);
        }

        private SimpleStream(Collection<T> list) {
            this.list = list;
        }

        @SafeVarargs
        private SimpleStream(T... values) {
            this.list = Arrays.asList(values);
        }

        /**
         * 第二个 实现filter
         * 链式调用 返回的是当前这种对象
         *
         * @param predicate filter是过滤，入参是一个返回boolean 所以应该用Predicate
         */
        public SimpleStream<T> filter(Predicate<T> predicate) {
            // 为什么新建个list 函数式编程最好不动原有的数据
            List<T> result = new ArrayList<>();
            for (T t : list) {
                if (predicate.test(t)) {
                    result.add(t);
                }
            }
            return new SimpleStream<>(result);
        }

        /**
         * 第三个 实现map
         *
         * @param function map是映射，入参是一个 有一个返回值，所以应该用Function
         *                 它返回的不一定和原来是同一类型数据 所以需要在方法上添加一个泛型参数 R
         *                 因为返回值类型变了 所以生成的SimpleStream对象泛型参数应该和返回值一致
         */
        public <R> SimpleStream<R> map(Function<T, R> function) {
            List<R> result = new ArrayList<>();
            for (T t : list) {
                result.add(function.apply(t));
            }
            return new SimpleStream<>(result);
        }

        /**
         * 第四个 foreach
         *
         * @param consumer forEach是遍历消费数据，入参是一个 无返回值，所以应该用Consumer
         */
        public void forEach(Consumer<T> consumer) {
            for (T t : list) {
                consumer.accept(t);
            }
        }

        /**
         * 化简 化简呢就是将多个元素最后化简为一个
         */
        public T reduce(T init, BinaryOperator<T> binaryOperator) {
            T result = init;
            for (T t : list) {
                result = binaryOperator.apply(result, t);
            }
            return result;
        }

        /**
         * 收集
         * 1、我需要创建一个集合，但是集合是可变的是由调用方确定的，
         * 所以将可变的作为一个函数对象传进来，它有返回值 有入参吗，创建一个空集合需要入参吗？不需要 所以是一个supplier
         * 2、不同的容器添加元素的方法也是不同的，所以我需要对方将添加元素的方法作为函数对象传进来，
         * 另外我还需要它告诉我要保存的对象是谁，所以是两个参数，他需要返回对象吗？不需要，只是说将对象放入容器
         * 所以它是一个BiConsumer
         */
        public <R> R collect(Supplier<R> supplier, BiConsumer<R, T> biConsumer) {
            R result = supplier.get();
            for (T t : list) {
                biConsumer.accept(result, t);
            }
            return result;
        }
    }

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 取出奇数并且进行平方运算
        SimpleStream.of(list)
                .filter(x -> (x & 1) == 1)
                .map(x -> x * x)
                .forEach(System.out::println);
        System.out.println("===========================");
        // 化简 取最大值、取最小值、求和
        System.out.println("取最大值：" + SimpleStream.of(list)
                .reduce(Integer.MIN_VALUE, Math::max));
        System.out.println("取最小值" + SimpleStream.of(list)
                .reduce(Integer.MAX_VALUE, Math::min));
        System.out.println("求和" + SimpleStream.of(list)
                .reduce(0, Integer::sum));

        System.out.println("===========================");

        System.out.println(SimpleStream.of(list)
                .collect(StringBuffer::new, StringBuffer::append).toString());

        System.out.println("===========================");
        List<Student> list1 = Arrays.asList(new Student("张三", 90, 18), new Student("李四", 80, 19), new Student("王五", 70, 16), new Student("赵六", 60, 17));

        SimpleStream.of(list1)
                .filter(student -> student.score() > 60)
                .collect(HashMap::new, (map, student) -> {
                    map.put(student.name(), student);
                }).forEach((k, v) -> System.out.println(k + ":" + v));

    }

    record Student(String name, int score, int age) {}

}
